==================================
Improved Wait Circuit for the ZX81
Wilf Rigter (7/2005)
==================================

Ever wondered about the purpose of TR1 in the ZX81 schematic?

TR1 together with some passive parts are connected to the CPU WAIT, NMI and
HALT lines and generates WAIT states when a NMI pulse occurs.

Ok, but why generate WAIT states? The short answer is to synchronize the CPU to
VIDEO timing. Still interested? Then read on to understand exactly why and how
this circuit works and how, with a small hardware mod, we finally correct a bug
to let us execute slow mode user programs 10% faster (about a quarter century
too late).

First note that this TR1 circuit is not needed for FAST mode video, which is
entirely generated in software with an overall fixed execution time.

In the SLOW mode, the video display is interleaved with user program execution
during blank lines at the top and bottom of the screen. The NMI generator is
used to periodically interrupt the user program to determine if it is time to
service the video. The CPU response to the NMI pulse can have a latency (and
jitter) of up to 23 clock cycles depending on which opcode is interrupted.
During user program execution, this jitter does not affect video timing of the
blank lines which is controlled by the horizontal sync pulses generated by the
NMI hardware in the ULA.

When the time comes to generate the video characters on the screen or generate
the vertical sync pulse then this NMI response jitter must be eliminated.

This is done when the NMI routine jumps to the NMI CONT routine, executes the
HALT at address $0079 and then waits for one more NMI pulse to exit the HALT
state.

NMI CONTINUED SUBROUTINE

006F  EX   AF,AF'        ; Retrieve main register AF.
0070  PUSH AF            ; Now save all application
0071  PUSH BC            ; program registers.
0072  PUSH DE
0073  PUSH HL
0074  LD   HL,(DFILE)    ; Needed only if IX=0281 and
0077  SET  7,H           ; if DFILE is executed.
0079  HALT               ; CPU T-state/video synchronization.
007A  OUT ($FD),A        ; Turn off NMI generator.
007C  JP  (IX)           ; Jump to VIDEO-1 or VIDEO-2.


However there is a problem: while halted, the HALT opcode executes NOPs every
4 clock cycles (T1-T4) and it can be at any clock cycle when NMI arrives but
does not respond to NMI until the rising edge of the T4 state. This means HALT
itself has a latency of up to 4 clock cycles before it exits the HALT state at
T4 in response to the NMI pulse.

So what is so important about 4 clock cycle jitter?

The problem is that ZX81 SLOW mode video is displayed by executing NOP
instructions. The execution time of a NOP is 4 clock cycles which is the time
it takes to clock out 1 byte of pixel data but the loading of that byte into
the video register must occur on a specific edge of 1 of those 4 clock cycles
to avoid jitter of the video display screen. Since this part of the video is
under real time CPU control, the CPU must be internally synchronized to one
edge of a specific clock cycle at the rising edge of NMI to ensure that each
displayed video frame starts in exactly the same place on the screen.

To do this the TR1 circuit was added that acts like an OR gate with an active
low WAIT output at the collector of TR1 when NMI is low and HALT is high.

The WAIT state is asserted after the HALT line goes high when NMI forces the
HALT exit, and inserts WAIT state between T2 and T3 of the M1 cycle of the NMI
response (RST to $0066) for the duration of the NMI pulse. Then on the rising
edge of NMI, the WAIT signal is removed and the CPU is precisely synchronized
with the start of T3 on the rising edge of the NMI pulse eliminating all video
jitter.

While the original WAIT circuit solves one problem it comes at a price: The
WAIT line is also asserted by every NMI pulse during blank line user program
execution and this reduces available CPU time by 10% !

An improved WAIT circuit adds just one 10K resistor and PNP transistor circuit
to correct this problem and increases SLOW mode program speed by 10%.

The new WAIT circuit only generates WAIT states when synchronizing the CPU with
NMI after the exit of HALT at address $0079. Since that occurs just twice
during each ZX81 video screen display there is no time wasted in generating
unnecessary WAIT states during user program execution.

So with a few hints and few pennies worth of parts, the ZX81 could have been
running on all cylinders. We can only imaging the millions of MIPS wasted by
the old WAIT circuit. The question is now whose head(s) should roll?

Enjoy

Wilf